﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Xsl;
using System.Xml;
using DSLFactory.VSCTDesigner.Properties;
using System.Xml.XPath;
using System.IO;
using Microsoft.VisualStudio.Modeling;
using Microsoft.Win32;
using Microsoft.VisualStudio.Modeling.Validation;

namespace DSLFactory.VSCTDesigner
{
    partial class VSCTDesignerSerializationHelper
    {
        private string _tempFileName;

        /// <summary>
        /// Loads the model.
        /// </summary>
        /// <param name="serializationResult">The serialization result.</param>
        /// <param name="partition">The partition.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="schemaResolver">The schema resolver.</param>
        /// <param name="validationController">The validation controller.</param>
        /// <returns></returns>
        public override CommandTable LoadModel(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, Microsoft.VisualStudio.Modeling.Partition partition, string fileName, Microsoft.VisualStudio.Modeling.ISchemaResolver schemaResolver, Microsoft.VisualStudio.Modeling.Validation.ValidationController validationController)
        {
            TransformVsct2Model(fileName);
            var model = base.LoadModel(serializationResult, partition, _tempFileName, schemaResolver, validationController);
            model.NativeCommandTable =  new VSCTLibraryWrapper(fileName);

            return model;
        }

        /// <summary>
        /// Transforms the VSCT2 model.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        private void TransformVsct2Model(string fileName)
        {
            // Transformation du modele initial
            _tempFileName = Path.GetTempFileName();

            XslCompiledTransform xslt = new XslCompiledTransform();
            // XSL
            XmlDocument xslDoc = new XmlDocument();
            xslDoc.LoadXml(Resources.VsctToModel);
            XmlReader xslReader = new XmlNodeReader(xslDoc);
            xslt.Load(xslReader, new XsltSettings(false, true), null);

            XPathDocument xmlReader = new XPathDocument(fileName);            
            using( XmlWriter output = XmlWriter.Create(_tempFileName) )
            {
                xslt.Transform(xmlReader, output);
            }            
        }

        /// <summary>
        /// Saves the model.
        /// </summary>
        /// <param name="serializationResult">The serialization result.</param>
        /// <param name="modelRoot">The model root.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="writeOptionalPropertiesWithDefaultValue">if set to <c>true</c> [write optional properties with default value].</param>
        public override void SaveModel(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, CommandTable modelRoot, string fileName, Encoding encoding, bool writeOptionalPropertiesWithDefaultValue)
        {
            base.SaveModel(serializationResult, modelRoot, _tempFileName, encoding, writeOptionalPropertiesWithDefaultValue);

            XPathDocument xmlReader = new XPathDocument(_tempFileName);
            using( XmlWriter output = XmlWriter.Create(fileName) )
            {
                TransformModel2Vsct(xmlReader, output);
            }
            File.Delete(_tempFileName);
        }

        /// <summary>
        /// Saves the model and diagram.
        /// </summary>
        /// <param name="serializationResult">The serialization result.</param>
        /// <param name="modelRoot">The model root.</param>
        /// <param name="modelFileName">Name of the model file.</param>
        /// <param name="diagram">The diagram.</param>
        /// <param name="diagramFileName">Name of the diagram file.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="writeOptionalPropertiesWithDefaultValue">if set to <c>true</c> [write optional properties with default value].</param>
        public override void SaveModelAndDiagram(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, CommandTable modelRoot, string modelFileName, VSCTDesignerDiagram diagram, string diagramFileName, Encoding encoding, bool writeOptionalPropertiesWithDefaultValue)
        {
            CustomSaveModelAndDiagram(serializationResult, modelRoot, modelFileName, diagram, diagramFileName, encoding, true);
        }

        public override CommandTable LoadModelAndDiagram(SerializationResult serializationResult, Store store, string modelFileName, string diagramFileName, ISchemaResolver schemaResolver, ValidationController validationController)
        {
            return base.LoadModelAndDiagram(serializationResult, store, modelFileName, diagramFileName, null, validationController);
        }
    }


    partial class VSCTDesignerSerializationHelperBase
    {
        private void OnPostLoadModel(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, Microsoft.VisualStudio.Modeling.Partition partition, string fileName, CommandTable modelRoot)
        {
            EnsureMinimalModel(modelRoot);
        }

        
        private static void EnsureMinimalModel(CommandTable modelRoot)
        {
            if( modelRoot.Commands == null )
                modelRoot.Commands = new Commands(modelRoot.Store);
            if( modelRoot.Bitmaps == null )
            {
                PropertyAssignment[] a = new PropertyAssignment[1];
                a[0] = new PropertyAssignment(ElementFactory.IdPropertyAssignment, new Guid("00000000-0000-0000-0000-000000000001"));
                modelRoot.Bitmaps = new Bitmaps(modelRoot.Store, a);
            }
        }

        private void OnPostLoadModelAndDiagram(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, Microsoft.VisualStudio.Modeling.Partition modelPartition, string modelFileName, Microsoft.VisualStudio.Modeling.Partition diagramPartition, string diagramFileName, CommandTable modelRoot, VSCTDesignerDiagram diagram)
        {
            EnsureMinimalModel(modelRoot);
        }

        /// <summary>
        /// Transforms the model2 VSCT.
        /// </summary>
        /// <param name="xmlReader">The XML reader.</param>
        /// <param name="output">The output.</param>
        protected void TransformModel2Vsct(XPathDocument xmlReader, XmlWriter output)
        {
            XslCompiledTransform xslt = new XslCompiledTransform();
            // XSL
            XmlDocument xslDoc = new XmlDocument();
            xslDoc.LoadXml(Resources.ModelToVsct);
            XmlReader xslReader = new XmlNodeReader(xslDoc);
            xslt.Load(xslReader);

            xslt.Transform(xmlReader, output);
        }
        
        // Astuce pour pouvoir appeler InternalSaveModel qui est privé
        protected void CustomSaveModelAndDiagram(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, CommandTable modelRoot, string modelFileName, VSCTDesignerDiagram diagram, string diagramFileName, Encoding encoding, bool writeOptionalPropertiesWithDefaultValue)
        {
            #region Check Parameters
            if( serializationResult == null )
                throw new global::System.ArgumentNullException("serializationResult");
            if( string.IsNullOrEmpty(modelFileName) )
                throw new global::System.ArgumentNullException("modelFileName");
            if( diagram == null )
                throw new global::System.ArgumentNullException("diagram");
            if( string.IsNullOrEmpty(diagramFileName) )
                throw new global::System.ArgumentNullException("diagramFileName");
            #endregion

            if( serializationResult.Failed )
                return;

            // Save the model file first
            using( global::System.IO.MemoryStream modelFileContent = this.InternalSaveModel(serializationResult, modelRoot, modelFileName, encoding, writeOptionalPropertiesWithDefaultValue) )
            {
                if( serializationResult.Failed )
                    return;

                using( global::System.IO.MemoryStream transformedModelFileContent = new MemoryStream() )
                {
                    using( XmlWriter transformedModelOutput = XmlWriter.Create(transformedModelFileContent) )
                    {
                        using( MemoryStream ms = new MemoryStream(modelFileContent.ToArray()) )
                        {
                            XPathDocument xmlReader = new XPathDocument(ms);
                            TransformModel2Vsct(xmlReader, transformedModelOutput);
                        }

                        using( global::System.IO.MemoryStream diagramFileContent = this.InternalSaveDiagram(serializationResult, diagram, diagramFileName, encoding, writeOptionalPropertiesWithDefaultValue) )
                        {
                            if( !serializationResult.Failed )
                            {
                                // Only write the contents if there's no error encountered during serialization.
                                if( modelFileContent != null )
                                {
                                    using( global::System.IO.FileStream fileStream = new global::System.IO.FileStream(modelFileName, global::System.IO.FileMode.Create, global::System.IO.FileAccess.Write, global::System.IO.FileShare.None) )
                                    {
                                        using( global::System.IO.BinaryWriter writer = new global::System.IO.BinaryWriter(fileStream, encoding) )
                                        {
                                            writer.Write(transformedModelFileContent.ToArray());
                                        }
                                    }
                                }

                                // Pas de sauvegarde du diagramme
                                //if( diagramFileContent != null )
                                //{
                                //    using( global::System.IO.FileStream fileStream = new global::System.IO.FileStream(diagramFileName, global::System.IO.FileMode.Create, global::System.IO.FileAccess.Write, global::System.IO.FileShare.None) )
                                //    {
                                //        using( global::System.IO.BinaryWriter writer = new global::System.IO.BinaryWriter(fileStream, encoding) )
                                //        {
                                //            writer.Write(diagramFileContent.ToArray());
                                //        }
                                //    }
                                //}
                            }
                        }
                    }
                }
            }
        }
    }
}
